class Sequel::Dataset
  module Sequelizer
    def format_eq_expression(l, r)
      case r
      when Range:
        r.exclude_end? ? \
          "(#{l} >= #{literal(r.begin)} AND #{l} < #{literal(r.end)})" : \
          "(#{l} >= #{literal(r.begin)} AND #{l} <= #{literal(r.end)})"
      when Array:
        "#{l} IN (#{literal(r)})"
      when Sequel::Dataset:
        "#{l} IN (#{r.sql})"
      when NilClass:
        "#{l} IS NULL"
      else
        "#{l} = #{literal(r)}"
      end
    end

    def call_expr(e, b)
      case e[2]
      when :>, :<, :>=, :<=
        l = pt_expr(e[1], b)
        r = pt_expr(e[3][1], b)
        "#{l} #{e[2]} #{r}"
      when :==, :=~
        l = eval_expr(e[1], b)
        r = eval_expr(e[3][1], b)
        format_eq_expression(l, r)
      when :nil?
        l = eval_expr(e[1], b)
        format_eq_expression(l, nil)
      when :like?
        l = pt_expr(e[1], b)
        r = pt_expr(e[3][1], b)
        "#{l} LIKE #{r}"
      else
        eval(RubyToRuby.new.process(e), b)
      end
    end

    def eval_expr(e, b)
      case e[0]
      when :call:
        call_expr(e, b)
      when :ivar, :cvar, :lvar, :dvar, :vcall, :const:
        eval(e[1].to_s, b)
      when :lit, :str: e[1]
      when :dot2: eval_expr(e[1], b)..eval_expr(e[2], b)
      when :dot3: eval_expr(e[1], b)...eval_expr(e[2], b)
      when :colon2: eval_expr(e[1], b).const_get(e[2])
      when :false: false
      when :true: true
      when :nil: nil
      when :match3:
        # regexp
        l = eval_expr(e[2], b)
        r = eval_expr(e[1], b)
        format_eq_expression(l, r)
      when :dasgn, :dasgn_curr:
        l = e[1]
        r = eval_expr(e[2], b)
        raise SequelError, "Invalid expression #{l} = #{r}. Did you mean :#{l} == #{r}?"
      else
        puts e.inspect
        raise SequelError, "Invalid expression tree: #{e.inspect}"
      end
    end

    def pt_expr(e, b)
      case e[0]
      when :not:
        if (e[1][0] == :lit) && (Symbol === e[1][1])
          # translate (!:x) into (x = 'f')
          format_eq_expression(e[1][1], false)
        else
          "NOT (#{pt_expr(e[1], b)})"
        end
      when :block, :and:
        e[1..-1].map {|i| "(#{pt_expr(i, b)})"}.join(" AND ")
      when :or: "(#{pt_expr(e[1], b)}) OR (#{pt_expr(e[2], b)})"
      when :call, :vcall:
        eval_expr(e, b)
      else
        literal(eval_expr(e, b))
      end
    end

    def translate_expr(proc)
      c = Class.new {define_method(:m, &proc)}
      pt_expr(ParseTree.translate(c, :m)[2][2], proc.binding)
    end
  end
end